/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "DecompressorWinPVR.h"
#include <Gluck.h>
#include <Endianess.h>
#include "CPVRTCDecompress.h"

#define PVR_TEXTURE_FLAG_TYPE_MASK	0xff
	
static char gPVRTexIdentifier[4] = {'P', 'V', 'R', '!' };
	
enum
{
	kPVRTextureFlagTypePVRTC_2 = 24,
	kPVRTextureFlagTypePVRTC_4
};
	
typedef struct _PVRTexHeader
	{
		unsigned int headerLength;
		unsigned int height;
		unsigned int width;
		unsigned int numMipmaps;
		unsigned int flags;
		unsigned int dataLength;
		unsigned int bpp;
		unsigned int bitmaskRed;
		unsigned int bitmaskGreen;
		unsigned int bitmaskBlue;
		unsigned int bitmaskAlpha;
		unsigned int pvrTag;
		unsigned int numSurfs;
} PVRTexHeader;

namespace liba
{
namespace pictureformat
{

DecompressorPVR::DecompressorPVR(const void * data, int size) : frameData(NULL), width(0), height(0)
{
	PVRTexHeader * header = NULL;
	unsigned int flags, pvrTag;
	unsigned int dataLength = 0, dataOffset = 0, dataSize = 0;
	unsigned int blockSize = 0, widthBlocks = 0, heightBlocks = 0;
	unsigned int bpp = 4;
	unsigned char *bytes = NULL;
	unsigned int formatFlags;
	
	header = (PVRTexHeader *)data;
	
	pvrTag = endianess::get_type_le(header->pvrTag);
	
	if (gPVRTexIdentifier[0] != ((pvrTag >>  0) & 0xff) ||
		gPVRTexIdentifier[1] != ((pvrTag >>  8) & 0xff) ||
		gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) ||
		gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff))
	{
		return;
	}
	
	flags = endianess::get_type_le(header->flags);
	formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK;
	
	//console tool gives different format flag for PVRTC4
	if(formatFlags == 13)
		formatFlags = kPVRTextureFlagTypePVRTC_4;
	
	if (formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypePVRTC_2)
	{
		width = endianess::get_type_le(header->width);
		height = endianess::get_type_le(header->height);
		
		dataLength = endianess::get_type_le(header->dataLength);
		
		bytes = ((unsigned char*)data) + sizeof(PVRTexHeader);
		
		// Calculate the data size for each texture level and respect the minimum number of blocks
		//while (dataOffset < dataLength)
		{
			if (formatFlags == kPVRTextureFlagTypePVRTC_4)
			{
				blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
				widthBlocks = width / 4;
				heightBlocks = height / 4;
				bpp = 4;
			}
			else
			{
				blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
				widthBlocks = width / 8;
				heightBlocks = height / 4;
				bpp = 2;
			}
			
			// Clamp to minimum number of blocks
			if (widthBlocks < 2)
				widthBlocks = 2;
			if (heightBlocks < 2)
				heightBlocks = 2;
			
			dataSize = widthBlocks * heightBlocks * ((blockSize  * 32) / 8);

			frameData = new unsigned char[dataSize];
			currentData = frameData;

			PVRTCDecompress(((unsigned char*)bytes) + dataOffset, false, width, height, frameData);
		}
	}
}

DecompressorPVR::~DecompressorPVR()
{
	if(frameData != NULL)
		delete [] frameData;
}

int DecompressorPVR::get_width(int frame)const
{
	return width;
}

int DecompressorPVR::get_height(int frame)const
{
	return height;
}

Format DecompressorPVR::get_best_format()const
{
	return A8R8G8B8;
}

int DecompressorPVR::get_frame_count()const
{
	return 1;
}

void DecompressorPVR::reset(Format f, int frame)
{
	currentData = frameData;
}

void DecompressorPVR::unpack_scanline(void * dst)
{
	convert_scanline<R8G8B8A8>(A8R8G8B8, currentData, dst, get_width(0));
	currentData += 4 * get_width(0);
}

void DecompressorPVR::skip_scanline(int count)
{
	currentData += count * 4 * get_width(0);
}

}
}